#pragma once

#include <mcp/config.h>
#include <mcp/forward.h>
#include <cjson/cJSON.h>

#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <memory>

namespace mcp {
namespace json {

// C++11 compatible expected type
template<typename T, typename E>
class expected {
private:
    union {
        T value_;
        E error_;
    };
    bool has_value_;

public:
    expected(const T& value) : value_(value), has_value_(true) {}
    expected(T&& value) : value_(std::move(value)), has_value_(true) {}
    expected(const E& error) : error_(error), has_value_(false) {}
    expected(E&& error) : error_(std::move(error)), has_value_(false) {}

    ~expected() {
        if (has_value_) {
            value_.~T();
        } else {
            error_.~E();
        }
    }

    expected(const expected& other) : has_value_(other.has_value_) {
        if (has_value_) {
            new (&value_) T(other.value_);
        } else {
            new (&error_) E(other.error_);
        }
    }

    expected(expected&& other) noexcept : has_value_(other.has_value_) {
        if (has_value_) {
            new (&value_) T(std::move(other.value_));
        } else {
            new (&error_) E(std::move(other.error_));
        }
    }

    expected& operator=(const expected& other) {
        if (this != &other) {
            if (has_value_) {
                value_.~T();
            } else {
                error_.~E();
            }
            has_value_ = other.has_value_;
            if (has_value_) {
                new (&value_) T(other.value_);
            } else {
                new (&error_) E(other.error_);
            }
        }
        return *this;
    }

    expected& operator=(expected&& other) noexcept {
        if (this != &other) {
            if (has_value_) {
                value_.~T();
            } else {
                error_.~E();
            }
            has_value_ = other.has_value_;
            if (has_value_) {
                new (&value_) T(std::move(other.value_));
            } else {
                new (&error_) E(std::move(other.error_));
            }
        }
        return *this;
    }

    explicit operator bool() const { return has_value_; }
    bool has_value() const { return has_value_; }

    T& value() & { return value_; }
    const T& value() const& { return value_; }
    T&& value() && { return std::move(value_); }
    const T&& value() const&& { return std::move(value_); }

    E& error() & { return error_; }
    const E& error() const& { return error_; }
    E&& error() && { return std::move(error_); }
    const E&& error() const&& { return std::move(error_); }
};

template<typename E>
class expected<void, E> {
private:
    union {
        E error_;
    };
    bool has_value_;

public:
    expected() : has_value_(true) {}
    expected(const E& error) : error_(error), has_value_(false) {}
    expected(E&& error) : error_(std::move(error)), has_value_(false) {}

    ~expected() {
        if (!has_value_) {
            error_.~E();
        }
    }

    expected(const expected& other) : has_value_(other.has_value_) {
        if (!has_value_) {
            new (&error_) E(other.error_);
        }
    }

    expected(expected&& other) noexcept : has_value_(other.has_value_) {
        if (!has_value_) {
            new (&error_) E(std::move(other.error_));
        }
    }

    expected& operator=(const expected& other) {
        if (this != &other) {
            if (!has_value_) {
                error_.~E();
            }
            has_value_ = other.has_value_;
            if (!has_value_) {
                new (&error_) E(other.error_);
            }
        }
        return *this;
    }

    expected& operator=(expected&& other) noexcept {
        if (this != &other) {
            if (!has_value_) {
                error_.~E();
            }
            has_value_ = other.has_value_;
            if (!has_value_) {
                new (&error_) E(std::move(other.error_));
            }
        }
        return *this;
    }

    explicit operator bool() const { return has_value_; }
    bool has_value() const { return has_value_; }

    E& error() & { return error_; }
    const E& error() const& { return error_; }
    E&& error() && { return std::move(error_); }
    const E&& error() const&& { return std::move(error_); }
};

/// @brief C++11 compatible optional type
template<typename T>
class optional {
private:
    union {
        T value_;
    };
    bool has_value_;

public:
    optional() : has_value_(false) {}
    optional(const T& value) : value_(value), has_value_(true) {}
    optional(T&& value) : value_(std::move(value)), has_value_(true) {}
    optional(const std::nullptr_t) : has_value_(false) {}

    ~optional() {
        if (has_value_) {
            value_.~T();
        }
    }

    optional(const optional& other) : has_value_(other.has_value_) {
        if (has_value_) {
            new (&value_) T(other.value_);
        }
    }

    optional(optional&& other) noexcept : has_value_(other.has_value_) {
        if (has_value_) {
            new (&value_) T(std::move(other.value_));
        }
    }

    optional& operator=(const optional& other) {
        if (this != &other) {
            if (has_value_) {
                value_.~T();
            }
            has_value_ = other.has_value_;
            if (has_value_) {
                new (&value_) T(other.value_);
            }
        }
        return *this;
    }

    optional& operator=(optional&& other) noexcept {
        if (this != &other) {
            if (has_value_) {
                value_.~T();
            }
            has_value_ = other.has_value_;
            if (has_value_) {
                new (&value_) T(std::move(other.value_));
            }
        }
        return *this;
    }

    explicit operator bool() const { return has_value_; }
    bool has_value() const { return has_value_; }

    T& operator*() & { return value_; }
    const T& operator*() const& { return value_; }
    T&& operator*() && { return std::move(value_); }
    const T&& operator*() const&& { return std::move(value_); }

    T* operator->() { return &value_; }
    const T* operator->() const { return &value_; }

    T& value() & { return value_; }
    const T& value() const& { return value_; }
    T&& value() && { return std::move(value_); }
    const T&& value() const&& { return std::move(value_); }

    template<typename U>
    T value_or(U&& default_value) const {
        return has_value_ ? value_ : static_cast<T>(std::forward<U>(default_value));
    }
};

/// @brief JSON value types
enum class ValueType {
    Null,
    Boolean,
    Number,
    String,
    Array,
    Object
};

/// @brief JSON error codes
enum class JsonErrorType {
    ParseError,
    InvalidType,
    OutOfRange,
    KeyNotFound,
    InvalidIndex,
    SyntaxError
};

/// @brief JSON error information
class JsonError {
public:
    JsonError(JsonErrorType type, std::string message, size_t position = 0)
        : type_(type), message_(std::move(message)), position_(position) {}

    JsonErrorType type() const noexcept { return type_; }
    const std::string& message() const noexcept { return message_; }
    size_t position() const noexcept { return position_; }

    const char* what() const noexcept { return message_.c_str(); }

private:
    JsonErrorType type_;
    std::string message_;
    size_t position_;
};

/// @brief JSON value class with copy-on-write semantics
class Value {
public:
    // Constructors
    Value() noexcept;
    explicit Value(bool b) noexcept;
    explicit Value(double n) noexcept;
    explicit Value(int64_t n) noexcept;
    explicit Value(const std::string& s);
    explicit Value(std::string&& s) noexcept;
    explicit Value(const char* s);
    explicit Value(std::vector<Value> array) noexcept;
    explicit Value(std::map<std::string, Value> object) noexcept;

    // Internal constructor for cJSON wrapping
    Value(cJSON* json, bool take_ownership) noexcept;

    // Copy and move
    Value(const Value& other);
    Value(Value&& other) noexcept;
    Value& operator=(const Value& other);
    Value& operator=(Value&& other) noexcept;

    // Destructor
    ~Value();

    // Type information
    ValueType type() const noexcept;
    bool is_null() const noexcept { return type() == ValueType::Null; }
    bool is_boolean() const noexcept { return type() == ValueType::Boolean; }
    bool is_number() const noexcept { return type() == ValueType::Number; }
    bool is_string() const noexcept { return type() == ValueType::String; }
    bool is_array() const noexcept { return type() == ValueType::Array; }
    bool is_object() const noexcept { return type() == ValueType::Object; }

    // Value accessors
    bool as_bool() const;
    double as_double() const;
    int64_t as_int64() const;
    const std::string& as_string() const;
    const std::vector<Value>& as_array() const;
    const std::map<std::string, Value>& as_object() const;

    // Modifiers
    std::vector<Value>& as_array();
    std::map<std::string, Value>& as_object();

    // Array access
    size_t size() const;
    bool empty() const;
    Value& operator[](size_t index);
    const Value& operator[](size_t index) const;
    void push_back(Value value);
    void resize(size_t size);

    // Object access
    Value& operator[](const std::string& key);
    Value& operator[](const char* key);
    const Value& operator[](const std::string& key) const;
    const Value& operator[](const char* key) const;
    bool contains(const std::string& key) const;
    size_t erase(const std::string& key);

    // Iterators for arrays
    auto array_begin() -> decltype(as_array().begin());
    auto array_end() -> decltype(as_array().end());
    auto array_begin() const -> decltype(as_array().begin());
    auto array_end() const -> decltype(as_array().end());

    // Iterators for objects
    auto object_begin() -> decltype(as_object().begin());
    auto object_end() -> decltype(as_object().end());
    auto object_begin() const -> decltype(as_object().begin());
    auto object_end() const -> decltype(as_object().end());

    // Parsing and serialization
    static expected<Value, JsonError> parse(const std::string& json);
    static expected<Value, JsonError> parse(const char* data, size_t size);
    std::string serialize() const;
    std::string serialize_pretty(int indent = 2) const;

    // Comparison
    bool operator==(const Value& other) const;
    bool operator!=(const Value& other) const;

private:
    cJSON* cJSON_;
    bool is_owner_;
};

/// @brief JSON schema validation result
struct ValidationResult {
    bool valid;
    std::vector<std::string> errors;
};

/// @brief JSON schema class
class Schema {
public:
    Schema();
    ~Schema();

    // Builder pattern methods
    Schema& type(ValueType type);
    Schema& type(const std::string& custom_type);
    Schema& property(const std::string& name, Schema schema);
    Schema& required(const std::vector<std::string>& properties);
    Schema& description(const std::string& description);
    Schema& default_value(const Value& value);
    Schema& enum_values(const std::vector<Value>& values);
    Schema& minimum(double min);
    Schema& maximum(double max);
    Schema& min_length(size_t length);
    Schema& max_length(size_t length);
    Schema& pattern(const std::string& regex);

    // Schema operations
    Value to_json() const;
    static Schema from_json(const Value& json);
    ValidationResult validate(const Value& value) const;

    // Copy and move
    Schema(const Schema& other);
    Schema(Schema&& other) noexcept;
    Schema& operator=(const Schema& other);
    Schema& operator=(Schema&& other) noexcept;

private:
    struct Impl;
    std::unique_ptr<Impl> impl_;
};

// Helper functions for creating common schema types
inline Schema object_schema() {
    return Schema().type(ValueType::Object);
}

inline Schema array_schema() {
    return Schema().type(ValueType::Array);
}

inline Schema string_schema() {
    return Schema().type(ValueType::String);
}

inline Schema number_schema() {
    return Schema().type(ValueType::Number);
}

inline Schema boolean_schema() {
    return Schema().type(ValueType::Boolean);
}

} // namespace json
} // namespace mcp